﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;

using P2PLib.Common.DataModel;
using P2PLib.Client.DataModel;
using P2PLib.Client.Command;

namespace P2PLib.Client
{
    /// <summary>
    /// each file server represent a file that can 
    /// be downloaded
    /// </summary>
    public class FileServer
    {
        private List<CClientManager> downloadQueue;
        private LocalFileInfo file;
        private static int limit=5;
        private BackgroundWorker bgListener;
        private int port;
        private Socket socket;
        private ClientInfo info;
        private string ip;

        #region Property
        public LocalFileInfo File
        {
            set
            {
                this.file = value;
            }
            get
            {
                return this.file;
            }
        }

        public static int Limit
        {
            set
            {
                limit = value;
            }
            get
            {
                return limit;
            }
        }

        public int Port
        {
            get { return this.port; }
            set { this.port = value; }
        }
        #endregion //property


        public FileServer(LocalFileInfo fl,int p)
        {
            this.file = fl;
            this.port = p;
            string strHostName = Dns.GetHostName(); //得到本机的主机名
            IPHostEntry ipEntry = Dns.GetHostByName(strHostName); //取得本机IP
            ip = ipEntry.AddressList[0].ToString(); //假设本地主机为单网卡 

        }



        public void StartServer()
        {
            Debug.WriteLine("FileServer:Starting file server..." + file.Path);

            bgListener = new BackgroundWorker();
            downloadQueue = new List<CClientManager>();

            bgListener.DoWork += new DoWorkEventHandler(bgListener_DoWork);
            bgListener.WorkerSupportsCancellation = true;
            bgListener.RunWorkerAsync();
        }

        /// <summary>
        /// start listening to port
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void bgListener_DoWork(object sender, DoWorkEventArgs e)
        {
            socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
            socket.Bind(new IPEndPoint(IPAddress.Parse(ip),this.port));
            socket.Listen(20);
            while (true)
            {
                this.CreateNewCClientManage(socket.Accept());
            }
        }


        private void CreateNewCClientManage(Socket socket)
        {
            CClientManager cclient = new CClientManager(socket,null);
            this.downloadQueue.Add(cclient);
            cclient.StartSending+=new StartSendingEventHandler(this.StartSendingFile);

            cclient.RequestCurrentPosition+=new RequestCurrentPositionEventHandler(GetClientQueueLocation);

            Debug.WriteLine("new client entered download queue:" +ip);
        }


        /// <summary>
        /// get client's current postition in the download queue
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        public int GetClientQueueLocation(object sender,ClientEventArgs e)
        {
            int loc = -1;
            ClientInfo client = e.Client;

            for (int i=0; i < downloadQueue.Count; i++)
            {
                ClientInfo cl = downloadQueue.ElementAt(i).Client;
                if (cl.IP.Equals(client.IP))
                {
                    loc = i;
                    break;
                }
            }
            return loc;
        }


        public void StartSendingFile(object sender,SendingFileEventArgs e)
        {
            long position = e.StartPosition;
            CClientManager cManager = (CClientManager)sender;
            //start a new thread to do the sending work

            BackgroundWorker bgSender = new BackgroundWorker();
            bgSender.DoWork += new DoWorkEventHandler(bgSender_DoWork);
            bgSender.WorkerSupportsCancellation = true;
            bgSender.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgSender_RunWorkerCompleted);
            bgSender.RunWorkerAsync(new object[] { position, cManager });

        }

        void bgSender_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bool sendSuccess= (bool)e.Result;
            if (sendSuccess)
            {
                Debug.WriteLine("FileServer:send file succeeded...");
            }
            else
            {
                Debug.WriteLine("FileServer: send file failed....");
            }
        }

        void bgSender_DoWork(object sender, DoWorkEventArgs e)
        {
            Debug.WriteLine("FileServer: start sending file....");
            try
            {
                //get the arguments;
                object[] args = (object[])e.Argument;
                CClientManager cm = (CClientManager)args[1];
                long position = (long)args[0];

                //send start recieving command to remote client;
                CCStartRecievingCommand src = new CCStartRecievingCommand();
                cm.SendCommand(src);

                //open the file to be transfered
                FileStream fs = new FileStream(this.file.Path, FileMode.Open);

                fs.Seek(position, 0);

                //start writing data to the stream;

                int readBytes = 0;
                byte[] buffer = new byte[8192];
                while ((readBytes = fs.Read(buffer, 0, buffer.Length)) != 0&&cm.Connected)
                {
                    cm.NetStream.Write(buffer, 0, readBytes);
                }

                fs.Close();
                if(cm.Connected)
                    e.Result = true;
                else
                    e.Result=false;
            }
            catch(Exception ex)
            {
                Debug.WriteLine("FileServer:Exception:" + ex.Message);
                Debug.WriteLine("Fileserver:Stacktrack:" + ex.StackTrace);
                e.Result = false;
            }
        }

       

        public void RemoveClient(CClientManager cm)
        {
            lock (this)
            {
                Debug.WriteLine("removing client from the queue.." + cm.IP.ToString());
                this.downloadQueue.Remove(cm);
            }
        }
    }
}
